//////////////////////////////////////////////
// Node.h
//
//////////////////////////////////////////////

/// Defines / Macros -------------------------

#pragma once

/// Includes ---------------------------------

// nkExport
#include "../Dll/DllDefines.h"

// nkMemory
#include <NilkinsMemory/Containers/BufferCast.h>
#include <NilkinsMemory/Containers/StringView.h>

#include <NilkinsMemory/Pointers/UniquePtr.h>

// Standards
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

/// Internals --------------------------------

namespace nkExport
{
	union NodeValueData
	{
		int intValue ;
		unsigned int uintValue ;
		long long longValue ;
		unsigned long long ulongValue ;

		float floatValue ;
		double doubleValue ;

		bool boolValue ;
	} ;

	enum class NODE_VALUE_TYPE
	{
		NOT_VALUE_TYPE,
		BOOL_TYPE,
		STRING_TYPE,
		INT_TYPE,
		UINT_TYPE,
		LONG_TYPE,
		ULONG_TYPE,
		FLOAT_TYPE,
		DOUBLE_TYPE
	} ;

	enum class NODE_NATURE
	{
		EMPTY_NATURE = 0,
		VALUE_NATURE,
		OBJECT_NATURE,
		ARRAY_NATURE
	} ;
}

/// Class ------------------------------------
	
namespace nkExport
{
	class DLL_EXPORT_EXPORT Node NK_FINAL
	{
		public :

			// Destructor
			virtual ~Node () ;

			// Getters
			// Globals
			NODE_NATURE getNodeNature () const ;
			NODE_VALUE_TYPE getNodeValueType () const ;
			// Utils
			bool isValueTypeNumber () const ;
			bool hasChild (nkMemory::StringView key, NODE_VALUE_TYPE valueType) ;
			bool hasChild (nkMemory::StringView key, NODE_NATURE nature) ;
			// Value
			nkMemory::StringView getValueAsString () const ;
			int getValueAsInt () const ;
			unsigned int getValueAsUint () const ;
			long long getValueAsLong () const ;
			unsigned long long getValueAsUlong () const ;
			float getValueAsFloat () const ;
			double getValueAsDouble () const ;
			bool getValueAsBool () const ;
			// Object
			unsigned int getMemberCount () const ;
			nkMemory::StringView getMemberKey (unsigned int index) const ;
			Node* getMemberValue (unsigned int index) const ;
			Node* getMemberValue (nkMemory::StringView key) const ;
			Node* getMemberValue (nkMemory::StringView key, NODE_VALUE_TYPE valueType) const ;
			Node* getMemberValue (nkMemory::StringView key, NODE_NATURE nodeNature) const ;
			// Array
			unsigned int getArraySize () const ;
			Node* getArrayElement (unsigned int index) const ;
			Node* getArrayElement (unsigned int index, NODE_VALUE_TYPE valueType) const ;
			Node* getArrayElement (unsigned int index, NODE_NATURE nodeNature) const ;
			// User
			void* getUserData () const ;

			// Setters
			// Value
			void setAsString (nkMemory::StringView value) ;
			void setAsInt (int value) ;
			void setAsUint (unsigned int value) ;
			void setAsLong (long long value) ;
			void setAsUlong (unsigned long long value) ;
			void setAsFloat (float value) ;
			void setAsDouble (double value) ;
			void setAsBool (bool value) ;
			// Object
			Node* addMember (nkMemory::StringView key) ;
			void deleteMember (nkMemory::StringView key) ;
			// Array
			Node* addElement () ;
			// User data
			void setUserData (void* value) ;

			// Operators
			Node& operator= (const Node&) noexcept ;
			Node& operator= (Node&&) noexcept ;

		public :

			// Statics
			static nkMemory::UniquePtr<Node> create () noexcept ;
			static nkMemory::UniquePtr<Node> create (const Node& other) noexcept ;
			static nkMemory::UniquePtr<Node> create (Node&& other) noexcept ;

		private :

			// Constructors
			Node () noexcept ;
			Node (const Node&) noexcept ;
			Node (Node&&) noexcept ;

		private :
		
			// Attributs
			// On peut avoir des valeurs
			std::pair<NodeValueData, NODE_VALUE_TYPE> _value ;
			// Cas particulier pour own la mémoire
			std::string _valueStr ;
			// Si on est un object
			std::unordered_map<std::string, nkMemory::UniquePtr<Node>> _object ;
			// Ou un array
			nkMemory::BufferCast<nkMemory::UniquePtr<Node>> _array ;

			// Ce qu'on a dans le node actuellement
			NODE_NATURE _nodeNature ;

			// Données user possibles
			void* _userData  ;
	} ;
}